#include "CBL_XMLSerializer.h"

#include <string>
#include <wchar.h>

#include "CBL_Rtti.h"
#include "CBL_Debug.h"
#include "CBL_ClassFactory.h"
#include "CBL_PooledObject.h"
#include "CBL_PoolManager.h"
#include "CBL_StaticVector.h"

CB_NAMESPACE_START

// ---------------------------------------------------------------------------------
//	SXMLNameValuePair
// ---------------------------------------------------------------------------------

struct SXMLNameValuePair
{
    std::string m_Name;
	std::string m_Value;
};

// ---------------------------------------------------------------------------------
//	CXMLNode declaration
// ---------------------------------------------------------------------------------

class CXMLNode : public Memory::CPooledObject, private SXMLNameValuePair
{
public:

	typedef CStaticVector<SXMLNameValuePair> CAttributeVector;
    typedef CStaticVector<CXMLNode*>		 CNodeVector;

	CXMLNode() : m_pParent(NULL), m_pNextNode(NULL), m_pNextNodeIterator(NULL) {}
    ~CXMLNode();
    
public:
    
    void AddChild(CXMLNode& _rNode);
    void AddAttribute(const char* _pName, const char* _pValue);
    
    CXMLNode* GetFirstChildByName(const char* _pName);
	const std::string& GetAttributeValueByName(const char* _pName) const;
    
    const std::string& GetName() const  			{ return m_Name; }
    const std::string& GetValue() const 			{ return m_Value; }
    
    CXMLNode* GetParent()     						{ return m_pParent; }
    CXMLNode* GetNext()     						{ return m_pNextNode; }
    CXMLNode* GetNextIterator();
    
    const CAttributeVector& GetAttributes() const 	{ return m_Attributes; }
    const CNodeVector& GetChildren() const 		  	{ return m_Children; }
    
public:

	void ResetIterator() { m_pNextNodeIterator = m_pNextNode; }
    
    void SetName(const char* _pName)   { m_Name = _pName; }
    void SetValue(const char* _pValue) { m_Value = _pValue; }

public:

	static const std::string s_EmptyString;
    
private:

	CXMLNode* m_pParent;
	CXMLNode* m_pNextNode;
    CXMLNode* m_pNextNodeIterator;

	CAttributeVector m_Attributes;
    CNodeVector      m_Children;
};

// ---------------------------------------------------------------------------------
//	Fast strcmp
// ---------------------------------------------------------------------------------

bool StrEqual(const char* _pLeft, const char* _pRight)
{
	if ((_pLeft == NULL) || (_pRight == NULL)) 
    	return false;
    
    while ((*_pLeft != 0) && (*_pRight != 0))
    	if (*_pLeft++ != *_pRight++) 
        	return false;
        
    return true;
}

// ---------------------------------------------------------------------------------
//	CXMLNode implementation
// ---------------------------------------------------------------------------------

const std::string CXMLNode::s_EmptyString;

// ---------------------------------------------------------------------------------

CXMLNode::~CXMLNode()
{
	uint32 NumChildren = m_Children.Size();
	for (uint32 ChildIdx(0); ChildIdx < NumChildren; ++ChildIdx)
    {
        delete m_Children[ChildIdx];
    }
}

// ---------------------------------------------------------------------------------

void CXMLNode::AddChild(CXMLNode& _rNode)
{
	CXMLNode* pNode = &_rNode;
	CXMLNode* pLastNode = GetFirstChildByName(_rNode.GetName().c_str());
    
    if (pLastNode)
    {
    	CXMLNode* pNextNode = pLastNode->m_pNextNode;
        
        while (pNextNode)
        {
            pLastNode = pNextNode;
            pNextNode = pLastNode->m_pNextNode;
        }
        
        pLastNode->m_pNextNodeIterator = pNode;
        pLastNode->m_pNextNode = pNode;
    }
    
    pNode->m_pParent = this;
    m_Children.PushBack(pNode);
}

// ---------------------------------------------------------------------------------

void CXMLNode::AddAttribute(const char* _pName, const char* _pValue)
{
    SXMLNameValuePair NewPair;
    NewPair.m_Name  = _pName;
    NewPair.m_Value = _pValue;
    
    m_Attributes.PushBack(NewPair);
}

// ---------------------------------------------------------------------------------

CXMLNode* CXMLNode::GetFirstChildByName(const char* _pName)
{
    uint32 NumChildren = m_Children.Size();
	
    for (uint32 ChildIdx(0); ChildIdx < NumChildren; ++ChildIdx)
    {
    	if (StrEqual(m_Children[ChildIdx]->GetName().c_str(), _pName))
        	return m_Children[ChildIdx];
    }
    
    return NULL;
}

// ---------------------------------------------------------------------------------

const std::string& CXMLNode::GetAttributeValueByName(const char* _pName) const
{
    uint32 NumChildren = m_Attributes.Size();
	
    for (uint32 ChildIdx(0); ChildIdx < NumChildren; ++ChildIdx)
    {
        if (StrEqual(m_Attributes[ChildIdx].m_Name.c_str(), _pName))
        	return m_Attributes[ChildIdx].m_Value;
    }
    
    return s_EmptyString;
}

// ---------------------------------------------------------------------------------

CXMLNode* CXMLNode::GetNextIterator()
{
    CXMLNode* pNode = m_pNextNodeIterator;
    
    if (pNode)
    	m_pNextNodeIterator = pNode->GetNext();
    
    return pNode;
}

// ---------------------------------------------------------------------------------
//	CXMLSerializer
// ---------------------------------------------------------------------------------

CXMLSerializer::CXMLSerializer(EOutputType _Type, const IFileSystem& _rFileSystem, const char* _pFileName)
	: ISerializer(_Type)
	, m_ParserDataIdx(0)
    , m_ParserMode(ReadingHead)
    , m_pRoot(new CXMLNode())
{
	if (IsReading())
    {
        m_FileHandle = _rFileSystem.OpenFile(_pFileName, IFileSystem::OpenRead);
        
        uint32 FileSize = m_FileHandle->GetFileSizeByte();
        char* pData = new char[FileSize];
        
        m_FileHandle->Read(pData, FileSize);
        
        Parse(m_pRoot, pData, ReachedEOF);
        
        delete pData;
        m_FileHandle.Release();
    }
    else
    {
        m_FileHandle = _rFileSystem.OpenFile(_pFileName, IFileSystem::OpenWrite | IFileSystem::OpenCreate | IFileSystem::OpenTruncate);
    }
    
    m_pCurrentNode = m_pRoot;
}

// ---------------------------------------------------------------------------------

CXMLSerializer::CXMLSerializer(EOutputType _Type, const char* _pData)
	: ISerializer(_Type)
	, m_ParserDataIdx(0)
	, m_ParserMode(ReadingHead)
	, m_pRoot(new CXMLNode())
{
	if (IsReading())
    {
    	Parse(m_pRoot, _pData, ReachedEOF);
    }
    
    m_pCurrentNode = m_pRoot;
}

// ---------------------------------------------------------------------------------

CXMLSerializer::~CXMLSerializer()
{
	if (m_FileHandle.IsValid())
    {
    	std::string FileData = NodeToString(m_pRoot);
    	m_FileHandle->Write(FileData.c_str(), FileData.size());
        m_FileHandle.Release();
    }
    delete m_pRoot;
}

// ---------------------------------------------------------------------------------

Memory::CAutoBuffer<char> CXMLSerializer::Flush()
{
	Memory::CAutoBuffer<char> Buffer;
    
    if (IsWriting())
    {
    	std::string FileData = ToString();
        
    	if (m_FileHandle.IsValid())
        {
            m_FileHandle->Write(FileData.c_str(), FileData.size());
            m_FileHandle.Release();
        }
        else
        {
        	uint32 BufferSize = FileData.size();
            
         	char* pBuffer = new char[BufferSize];
        	memcpy(pBuffer, FileData.c_str(), BufferSize);
            
            Buffer.SetBuffer(pBuffer, BufferSize);
        }
    }
    
    return Buffer;
}

// ------------------------------------------------------------------------------------

const std::string& CXMLSerializer::ReadValueString(const char* _pName, uint32 _Flags)
{
	if (_Flags & FlagInlineBlockData)
    	return m_pCurrentNode->GetAttributeValueByName(_pName);
        
    CXMLNode* pNode = m_pCurrentNode->GetFirstChildByName(_pName);
    
    if (pNode && (_Flags & FlagCollectionItem))
    {
        pNode = pNode->GetNextIterator();
    }
    
    if (pNode)
    {
    	const std::string& rAttribute = pNode->GetAttributeValueByName("value");
        if (rAttribute.size() == 0)
        	return pNode->GetValue();
        
        return rAttribute;
    }
    
    Debug::Assert(CB_DEBUG_MSG((_Flags & FlagRequiredField) == 0, 
                  "Node \"%s\" is flagged as required and was not found."), 
                  _pName);
    
    return CXMLNode::s_EmptyString;
}

// ------------------------------------------------------------------------------------

void CXMLSerializer::AddValueString(const char* _pName, const char* _pValue, uint32 _Flags)
{
    if (_Flags & FlagInlineBlockData)
    { 
    	m_pCurrentNode->AddAttribute(_pName, _pValue);
    }
    else 
    {
        CXMLNode* pNode = new CXMLNode();
        pNode->SetName(_pName);
        pNode->AddAttribute("value", _pValue);
        
        m_pCurrentNode->AddChild(*pNode);
    }
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(bool& _rValue, const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {        
        const std::string& Value = ReadValueString(_pName, _Flags);
        
        if (Value.size() == 0)
        	_rValue = false;
        else
        	_rValue = (StrEqual(Value.c_str(), "true"));
    }
    else
    {
    	AddValueString(_pName, (_rValue) ? "true" : "false", _Flags);
    }
    
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(int8& _rValue, const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {        
        const std::string& Value = ReadValueString(_pName, _Flags);
        
        if (Value.size() == 0)
            _rValue = 0;
        else
            _rValue = (int8)atoi(Value.c_str());
    }
    else
    {
    	char Buffer[4];
    	sprintf(Buffer, "%d", _rValue);
        
        AddValueString(_pName, Buffer, _Flags);
    }
    
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(int16& _rValue, const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {        
        const std::string& Value = ReadValueString(_pName, _Flags);
        
        if (Value.size() == 0)
            _rValue = 0;
        else
            _rValue = (int16)atoi(Value.c_str());
    }
    else
    {
    	char Buffer[6];
    	sprintf(Buffer, "%d", _rValue);
        
        AddValueString(_pName, Buffer, _Flags);
    }
    
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(int32& _rValue, const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {        
        const std::string& Value = ReadValueString(_pName, _Flags);
        
        if (Value.size() == 0)
            _rValue = 0;
        else
            _rValue = atoi(Value.c_str());
    }
    else
    {
    	char Buffer[11];
    	sprintf(Buffer, "%d", _rValue);
        
        AddValueString(_pName, Buffer, _Flags);
    }
    
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(int64& _rValue, const char* _pName, uint32 _Flags)
{
    if (IsReading())
    {        
        const std::string& Value = ReadValueString(_pName, _Flags);
        
        if (Value.size() == 0)
            _rValue = 0;
        else
        	_rValue = CB_ATOI64(Value.c_str());
    }
    else
    {
    	char Buffer[20];
    	sprintf(Buffer, "%Ld", _rValue);
        
        AddValueString(_pName, Buffer, _Flags);
    }
    
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(uint8& _rValue, const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {        
        const std::string& Value = ReadValueString(_pName, _Flags);
        
        if (Value.size() == 0)
            _rValue = 0;
        else
            _rValue = (uint8)atoi(Value.c_str());
    }
    else
    {
    	char Buffer[4];
    	sprintf(Buffer, "%d", _rValue);
        
        AddValueString(_pName, Buffer, _Flags);
    }
    
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(uint16& _rValue, const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {        
        const std::string& Value = ReadValueString(_pName, _Flags);
        
        if (Value.size() == 0)
            _rValue = 0;
        else
            _rValue = (uint16)atoi(Value.c_str());
    }
    else
    {
    	char Buffer[6];
    	sprintf(Buffer, "%d", _rValue);
        
        AddValueString(_pName, Buffer, _Flags);
    }
    
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(uint32& _rValue, const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {        
        const std::string& Value = ReadValueString(_pName, _Flags);
        
        if (Value.size() == 0)
            _rValue = 0;
        else
            _rValue = atoi(Value.c_str());
    }
    else
    {
    	char Buffer[11];
    	sprintf(Buffer, "%d", _rValue);
        
        AddValueString(_pName, Buffer, _Flags);
    }
    
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(uint64& _rValue, const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {        
        const std::string& Value = ReadValueString(_pName, _Flags);
        
        if (Value.size() == 0)
            _rValue = 0;
        else
        	_rValue = CB_ATOI64(Value.c_str());
    }
    else
    {
    	char Buffer[20];
    	sprintf(Buffer, "%Ld", _rValue);
        
        AddValueString(_pName, Buffer, _Flags);
    }
    
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(uint32& _rValue, const char** _pTranslationStringArray, uint32 _ArrayLength, const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {        
        const std::string& Value = ReadValueString(_pName, _Flags);
    
        for (uint32 i(0); i<_ArrayLength; ++i)
        {
            if (Value == _pTranslationStringArray[i])
            {
                _rValue = i;
                return true; // ### return ###
            }
        }
        
        _rValue = 0;
    }
    else
    {
    	AddValueString(_pName, _pTranslationStringArray[_rValue], _Flags);
    }
    
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(float& _rValue, const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {        
        const std::string& Value = ReadValueString(_pName, _Flags);
        
        if (Value.size() == 0)
            _rValue = 0;
        else
            _rValue = (float)atof(Value.c_str());
    }
    else
    {
    	char Buffer[20];
    	sprintf(Buffer, "%f", _rValue);
        
        AddValueString(_pName, Buffer, _Flags);
    }
    
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(double& _rValue, const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {        
        const std::string& Value = ReadValueString(_pName, _Flags);
        
        if (Value.size() == 0)
            _rValue = 0;
        else
            _rValue = atof(Value.c_str());
    }
    else
    {
    	char Buffer[20];
    	sprintf(Buffer, "%f", _rValue);
        
        AddValueString(_pName, Buffer, _Flags);
    }
    
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(char* _pValue, size_t _BufferSize, const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {        
        const std::string& Value = ReadValueString(_pName, _Flags);
        strncpy(_pValue, Value.c_str(), _BufferSize);
    }
    else
    {
    	AddValueString(_pName, _pValue, _Flags);
    }
    
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(wchar_t* CB_UNUSED(_pValue), size_t CB_UNUSED(_BufferSize), const char* CB_UNUSED(_pName), uint32 CB_UNUSED(_Flags))
{
	// TODO
    return false;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(std::string& _rValue, const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {        
        const std::string& Value = ReadValueString(_pName, _Flags);
        _rValue = Value;
    }
    else
    {
    	AddValueString(_pName, _rValue.c_str(), _Flags);
    }
    
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(std::wstring& CB_UNUSED(_rValue), const char* CB_UNUSED(_pName), uint32 CB_UNUSED(_Flags))
{
	// TODO
    return false;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(ISerializeable& _rObject, const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {
    	CXMLNode* pNode = m_pCurrentNode->GetFirstChildByName(_pName);
        
    	if (pNode && (_Flags & FlagCollectionItem))
        {
        	pNode = pNode->GetNextIterator();
        }
        
    	if (pNode == NULL)
        {
        	Debug::Assert(CB_DEBUG_MSG((_Flags & FlagRequiredField) == 0, 
                          "Node \"%s\" is flagged as required and was not found."), 
                          _pName);
        
        	return false;
    	}
        
        m_pCurrentNode = pNode; 
        
    	_rObject.Serialize(*this);
    }
    else
    {
        CXMLNode* pNode = new CXMLNode();
        pNode->SetName(_pName);
        pNode->AddAttribute("class", RTTI::GetClassName(_rObject));
        
        m_pCurrentNode->AddChild(*pNode);
        m_pCurrentNode = pNode;
        
    	_rObject.Serialize(*this);
    }
    
    m_pCurrentNode = m_pCurrentNode->GetParent();
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::Serialize(ISerializeable** _ppObject, const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {
    	CXMLNode* pNode = m_pCurrentNode->GetFirstChildByName(_pName);
    
    	if (pNode && (_Flags & FlagCollectionItem))
        {
        	pNode = pNode->GetNextIterator();
        }
    
    	if (pNode == NULL)
        {
            Debug::Assert(CB_DEBUG_MSG((_Flags & FlagRequiredField) == 0, 
                          "Node \"%s\" is flagged as required and was not found."), 
                          _pName);
                          
        	return false;
    	}
        
    	m_pCurrentNode = pNode; 
        
    	if (*_ppObject == NULL)
        {
        	*_ppObject = static_cast<ISerializeable*>(CClassFactory::GetInstance().NewObject(pNode->GetAttributeValueByName("class").c_str()));
        }
    
    	(*_ppObject)->Serialize(*this);
    }
    else
    {
        CXMLNode* pNode = new CXMLNode();
        pNode->SetName(_pName);
        pNode->AddAttribute("class", RTTI::GetClassName(*_ppObject));
        
        m_pCurrentNode->AddChild(*pNode);
        m_pCurrentNode = pNode;
        
    	(*_ppObject)->Serialize(*this);
    }
    
    m_pCurrentNode = m_pCurrentNode->GetParent();
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::SerializeBlockStart(const char* _pName, uint32 _Flags)
{
	if (IsReading())
    {
    	CXMLNode* pNode = m_pCurrentNode->GetFirstChildByName(_pName);
    
    	if (pNode && (_Flags & FlagCollectionItem))
        {
        	pNode = pNode->GetNextIterator();
        }
        
    	if (pNode == NULL)
        {
        	Debug::Assert(CB_DEBUG_MSG((_Flags & FlagRequiredField) == 0, 
                          "Node \"%s\" is flagged as required and was not found."), 
                          _pName);
        
        	return false;
    	}
        
        m_pCurrentNode = pNode;
    }
    else
    {
    	CXMLNode* pNode = new CXMLNode();
        pNode->SetName(_pName);
        
        m_pCurrentNode->AddChild(*pNode);
        m_pCurrentNode = pNode;
    }
    
    return true;
}

// ---------------------------------------------------------------------------------

bool CXMLSerializer::SerializeBlockEnd(const char* CB_UNUSED(_pName), uint32 CB_UNUSED(_Flags))
{
	Debug::Assert(m_pCurrentNode != NULL);
	m_pCurrentNode = m_pCurrentNode->GetParent();
    return true;
}

// ---------------------------------------------------------------------------------
//	XML parser part
// ---------------------------------------------------------------------------------

void CXMLSerializer::AddToParserBuffer(char _Char)
{
    if (m_ParserMode != ReadingComment)
    	m_ParserBuffer[m_ParseBufferIdx++] = _Char;
}

// ---------------------------------------------------------------------------------

CXMLSerializer::EParserMode CXMLSerializer::Progress(const char* _pData)
{
	// State machine setup.
    // Progress will always check for up to NumTest SearchStrings, which are defined
    // by the current parser mode.
    //
    // The state transitions are bound to the test strings, so when one string matches
    // the corresponding state is triggered.
    //
    // Characters that do not match any test string (at the current position) are stored
    // in m_ParserBuffer. AttributeNames are stored in m_AttributeNameBuffer as Attributes
    // have to be set as key-value pairs.
    
	static const int NumTests = 3;
    
    static const char* SearchStrings[NumParserModes][NumTests] = {
        { "<*",  "<!--", "<?" }, // ReadingHead
        { "<*",  "<!--", "</" }, // ReadingTag
        { "?>",  NULL,   NULL }, // ReadingMeta
        { " ",   "/>",   ">"  }, // ReadingNodeName
        { "=",   "/>",   ">"  }, // ReadingAttributeName
        { "\"",  NULL,   NULL }, // ReadingAttributeStart
        { "\"",  NULL,   NULL }, // ReadingAttribute
        { "-->", NULL,   NULL }, // ReadingComment
        { "<*",  "<!--", "</" }, // ReadingBlock
        { ">",   NULL,   NULL }, // ReadingCloseBlock
        { NULL,  NULL,   NULL }, // ReachedEOB
        { NULL,  NULL,   NULL }, // ReachedEOF
    };
    
    static const EParserMode ModeSwitch[NumParserModes][NumTests] = {
        { ReadingNodeName, 		 ReadingComment,  	ReadingMeta			}, // ReadingHead
        { ReadingNodeName, 		 ReadingComment,	ReadingCloseBlock	}, // ReadingTag
        { ReadingTag,  			 ReachedEOF,		ReachedEOF			}, // ReadingMeta
        { ReadingAttributeName,  ReadingTag, 	 	ReadingBlock		}, // ReadingNodeName
        { ReadingAttributeStart, ReadingTag, 	 	ReadingBlock		}, // ReadingAttributeName
        { ReadingAttribute, 	 ReachedEOF,		ReachedEOF			}, // ReadingAttributeStart
        { ReadingAttributeName,  ReachedEOF,		ReachedEOF			}, // ReadingAttribute
        { ReadingTag,  			 ReachedEOF,		ReachedEOF			}, // ReadingComment
        { ReadingNodeName, 		 ReadingComment,  	ReadingCloseBlock	}, // ReadingBlock
        { ReadingTag, 			 ReachedEOF,		ReachedEOF			}, // ReadingCloseBlock
        { ReachedEOF, 			 ReachedEOF,		ReachedEOF			}, // ReachedEOB
        { ReachedEOF, 			 ReachedEOF,		ReachedEOF			}, // ReachedEOF
    };
    
    static const int32 Complete[NumParserModes][NumTests] = {
        { 2,  4,  2 }, // ReadingHead
        { 2,  4,  2 }, // ReadingTag
        { 2, -1, -1 }, // ReadingMeta
        { 1,  2,  1 }, // ReadingNodeName
        { 1,  2,  1 }, // ReadingAttributeName
        { 1, -1, -1 }, // ReadingAttributeStart
        { 1, -1, -1 }, // ReadingAttribute
        { 3, -1, -1 }, // ReadingComment
        { 2,  4,  2 }, // ReadingBlock
        { 1, -1, -1 }, // ReadingCloseBlock
        {-1, -1, -1 }, // ReachedEOB
        {-1, -1, -1 }, // ReachedEOF
    };
    
    EParserMode ReturnMode;
    
    uint32 FindIdx[NumTests] = { 0, 0, 0 };
    
    if (m_ParserMode != ReadingComment)
    {
    	m_ParserBuffer[0] = 0;
    	m_ParseBufferIdx  = 0;
    }
    
    const char** pSearch     = SearchStrings[m_ParserMode];
    const int32* pComplete  = Complete[m_ParserMode];
    
    for (int Guard(0); Guard < sizeof(m_ParserBuffer); ++Guard)
    {
    	bool Match           = false;
        bool ReturnOnNoMatch = false;
        char CurrentDataChar = _pData[m_ParserDataIdx];
        
        if (CurrentDataChar == '\0')
        {
        	AddToParserBuffer('\0');    
        	return ReachedEOF; // ### return (end of file) ###
        }
        
    	for (int Test(0); Test<NumTests; ++Test)
        {
        	if (pSearch[Test] != NULL)
            {
                if (CurrentDataChar == pSearch[Test][FindIdx[Test]])
                {
                    ++FindIdx[Test];
                    
                    if (FindIdx[Test] == (uint32)pComplete[Test])
                    {
                    	AddToParserBuffer('\0');
                        ++m_ParserDataIdx;
                    	return ModeSwitch[m_ParserMode][Test]; // ### return (full match) ###
                    }
                    
                	Match = true;
                }
                else if (pSearch[Test][FindIdx[Test]] == '*')
                {
                    ReturnOnNoMatch = true;
                    ReturnMode      = ModeSwitch[m_ParserMode][Test];
                }
                else
                {
                    FindIdx[Test] = 0;
                }
            }
            else break; // Stop when a test string is NULL (no more strings tests)
        }
        
    	if (!Match)
        {
        	if (ReturnOnNoMatch)
            {
            	// Do not increment m_ParserDataIdx. Wildcards must be read again.
            	AddToParserBuffer('\0');
            	return ReturnMode; // ### return (full wildcard match) ###
            }
            
        	if 	((CurrentDataChar != '\n') && 
                 (CurrentDataChar != '\r') && 
                 (CurrentDataChar != '\t') &&
            	 ((CurrentDataChar != ' ') || (m_ParserMode == ReadingAttribute) || (m_ParserMode == ReadingBlock)))
            {
            	AddToParserBuffer(CurrentDataChar);
            }
        }
        
        ++m_ParserDataIdx;
    }
    
    Debug::Assert(false, "XML Text block too long.\nMust not be longer than 4 KB.");
    return ReachedEOF;
}

// ---------------------------------------------------------------------------------

void CXMLSerializer::Parse(CXMLNode* _pNode, const char* _pData, EParserMode _ReturnOnMode)
{
	EParserMode NextMode;
    m_pCurrentParent = _pNode;
    
    do
    {
    	NextMode = Progress(_pData);
        
        // React on a state change
        // Note the m_ParserMode contains the current Mode, so OnReading*() can check from
        // which state we're coming.
        
        switch (NextMode)
        {
        case ReadingTag:
            OnReadingTag();
            break;
            
        case ReadingMeta:
            OnReadingMeta();
            break;
            
        case ReadingNodeName:
            OnReadingNodeName();
            break;
            
        case ReadingAttributeName:
            OnReadingAttributeName();
            break;
            
        case ReadingAttributeStart:
        	OnReadingAttributeStart();
            break;
            
        case ReadingAttribute:
            OnReadingAttribute();
            break;
            
        case ReadingComment:
            OnReadingComment();
            break;
            
        case ReadingBlock:
            OnReadingBlock(_pData);
            break;
            
        case ReadingCloseBlock:
            OnReadingCloseBlock();
            break;
            
        case ReachedEOF:
        	m_ParserMode = ReachedEOF;
        	break;
            
        default:
        	break;
        }
        
    	if ((m_ParserMode != ReachedEOF) && (m_ParserMode != ReachedEOB))
        	m_ParserMode = NextMode;
    }
    while ((m_ParserMode != _ReturnOnMode) && (m_ParserMode != ReachedEOF));
    
    if (m_ParserMode == ReachedEOB)
    	m_ParserMode = ReadingTag;
}

// ---------------------------------------------------------------------------------

void CXMLSerializer::OnReadingTag()
{
	switch (m_ParserMode)
    {
    case ReadingNodeName:
    	if (Debug::Verify(StrEqual(m_pCurrentParent->GetName().c_str(), m_ParserBuffer), 
        				  "Close block name mismatch: %s should be %s.", 
                          m_ParserBuffer, m_pCurrentParent->GetName().c_str()))
        {
            m_ParserMode = ReachedEOB;  
        }
        else
        {
        	m_ParserMode = ReachedEOF;
        }
        break;
        
    case ReadingCloseBlock:
    	if (Debug::Verify(StrEqual(m_pCurrentParent->GetName().c_str(), m_ParserBuffer), 
        				  "Close block name mismatch: %s should be %s.", 
                          m_ParserBuffer, m_pCurrentParent->GetName().c_str()))
        {
        	m_ParserMode = ReachedEOB;  
        }
        else
        {
        	m_ParserMode = ReachedEOF;
        }
        break;
        
    case ReadingMeta:
    	m_pCurrentParent->AddAttribute(m_AttributeNameBuffer, m_ParserBuffer);
        break;
        
    case ReadingAttributeName:
        if (Debug::Verify(m_pCurrentNode != NULL, "No node for attribute %s.", m_AttributeNameBuffer))
        {
        	m_pCurrentParent->AddChild(*m_pCurrentNode);
        	m_pCurrentNode = NULL;
    	}
        break;
        
    default:
    	break;
    }
}


// ---------------------------------------------------------------------------------

void CXMLSerializer::OnReadingBlock(const char* _pData)
{
	switch (m_ParserMode)
    {
    case ReadingNodeName:
    	if (Debug::Verify((m_pCurrentNode != NULL) && (m_pCurrentNode->GetName().size() == 0), "Nameless node."))
        {
        	m_pCurrentNode->SetName(m_ParserBuffer);
        }
        else 
        {
        	m_ParserMode = ReachedEOF;
        }
        break;
        
    case ReadingAttribute:
    	if (Debug::Verify(m_pCurrentNode != NULL, "No node for attribute %s.", m_AttributeNameBuffer))
        	m_pCurrentNode->AddAttribute(m_AttributeNameBuffer, m_ParserBuffer);
    	break;
    	
    }
    
    if (Debug::Verify(m_pCurrentNode != NULL, "Nameless node."))
    {
    	m_pCurrentParent->AddChild(*m_pCurrentNode);
        CXMLNode* pRestore = m_pCurrentParent;
        
        m_ParserMode = ReadingBlock;        
    	Parse(m_pCurrentNode, _pData, ReachedEOB);
        
        m_pCurrentParent = pRestore;
        m_pCurrentNode   = NULL;
    }
    else
    {
        m_ParserMode = ReachedEOF;
    }
}

// ---------------------------------------------------------------------------------

void CXMLSerializer::OnReadingNodeName()
{
	switch (m_ParserMode)
    {
    case ReadingBlock:
    	m_pCurrentParent->SetValue(m_ParserBuffer);
        m_pCurrentNode = new CXMLNode();
        break;
        
    case ReadingTag:
    	m_pCurrentNode = new CXMLNode();
        break;
        
    case ReadingHead:
    	m_pCurrentNode = new CXMLNode();
        break;
        
    default:
    	break;
    }
}

// ---------------------------------------------------------------------------------

void CXMLSerializer::OnReadingAttributeName()
{
	switch (m_ParserMode)
    {
    case ReadingAttribute:
    	if (Debug::Verify(m_pCurrentNode != NULL, "No node for attribute %s.", m_AttributeNameBuffer))
        	m_pCurrentNode->AddAttribute(m_AttributeNameBuffer, m_ParserBuffer);
    	break;
        
    case ReadingNodeName:
    	m_pCurrentNode->SetName(m_ParserBuffer);
        break;
        
    default:
    	break;
    }
}

// ---------------------------------------------------------------------------------

void CXMLSerializer::OnReadingAttributeStart()
{
	if (Debug::Verify(m_ParserMode == ReadingAttributeName, "Nameless attribute."))
    	strcpy(m_AttributeNameBuffer, m_ParserBuffer);
}

// ---------------------------------------------------------------------------------

void CXMLSerializer::OnReadingAttribute()
{
}

// ---------------------------------------------------------------------------------

void CXMLSerializer::OnReadingComment()
{
}

// ---------------------------------------------------------------------------------

void CXMLSerializer::OnReadingCloseBlock()
{
	m_pCurrentParent->SetValue(m_ParserBuffer);    
}

// ---------------------------------------------------------------------------------

void CXMLSerializer::OnReadingMeta()
{
	strcpy(m_AttributeNameBuffer, "_meta");
}

// ---------------------------------------------------------------------------------

std::string CXMLSerializer::ToString()
{
    std::string Out;
    uint32 NumChildren = m_pRoot->GetChildren().Size();
    
    for (uint32 ChildIdx(0); ChildIdx < NumChildren; ++ChildIdx)
    {
        Out += NodeToString(m_pRoot->GetChildren()[ChildIdx]);
    }
    
    return Out;
}

// ---------------------------------------------------------------------------------

std::string CXMLSerializer::NodeToString(CXMLNode* _pNode, uint32 _Depth)
{
	std::string Out;
    std::string Indent;
    
    for (uint32 i(0); i<_Depth; ++i)
        Indent += "\t";
    
    Out += Indent;
    Out += "<";
    Out += _pNode->GetName();
    
    uint32 NumAttributes = _pNode->GetAttributes().Size();
    for (uint32 AttrIdx(0); AttrIdx < NumAttributes; ++AttrIdx)
    {
    	if (_pNode->GetAttributes()[AttrIdx].m_Name[0] != '_')
        {
        	Out += " ";
        	Out += _pNode->GetAttributes()[AttrIdx].m_Name;
        	Out += "=\"";
        	Out += _pNode->GetAttributes()[AttrIdx].m_Value;
        	Out += "\"";
        }
    }
    
    uint32 NumChildren = _pNode->GetChildren().Size();
    if (NumChildren > 0)
    {
        Out += ">\n";
        
        for (uint32 ChildIdx(0); ChildIdx < NumChildren; ++ChildIdx)
        {
            Out += NodeToString(_pNode->GetChildren()[ChildIdx], _Depth+1);
        }
        
        Out += Indent; 
        Out += "</";
        Out += _pNode->GetName();
        Out += ">\n";
    }
    else
    {
    	if (_pNode->GetValue().size() > 0)
        {
        	Out += ">";
            Out += _pNode->GetValue();
            Out += "</";
            Out += _pNode->GetName();
            Out += ">\n";    
        }
        else
        {
            Out += "/>\n";
        }
    }
    
    return Out;
}


CB_NAMESPACE_END